{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "[nltk_data] Downloading package punkt to /home/am/nltk_data...\n",
      "[nltk_data]   Package punkt is already up-to-date!\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from sklearn.model_selection import train_test_split\n",
    "from tensorflow.keras.callbacks import ModelCheckpoint\n",
    "from tensorflow.keras.layers import Input\n",
    "from tensorflow.keras.layers import RepeatVector\n",
    "\n",
    "from tensorflow.keras.layers import LSTM\n",
    "from tensorflow.keras.layers import Bidirectional\n",
    "from tensorflow.keras.models import Model\n",
    "from tensorflow.keras.preprocessing import sequence\n",
    "from scipy.stats import describe\n",
    "import collections\n",
    "import matplotlib.pyplot as plt\n",
    "import nltk\n",
    "import numpy as np\n",
    "import os\n",
    "from time import gmtime, strftime\n",
    "from tensorflow.keras.callbacks import TensorBoard\n",
    "import re\n",
    "\n",
    "# Needed to run only once\n",
    "nltk.download('punkt')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "def lookup_word2id(word):\n",
    "    try:\n",
    "        return word2id[word]\n",
    "    except KeyError:\n",
    "        return word2id[\"UNK\"]\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "def load_glove_vectors(glove_file, word2id, embed_size):\n",
    "    embedding = np.zeros((len(word2id), embed_size))\n",
    "    fglove = open(glove_file, \"rb\")\n",
    "    for line in fglove:\n",
    "        cols = line.strip().split()\n",
    "        word = cols[0].decode('utf-8')\n",
    "        if embed_size == 0:\n",
    "            embed_size = len(cols) - 1\n",
    "        if word in word2id:\n",
    "            vec = np.array([float(v) for v in cols[1:]])\n",
    "        embedding[lookup_word2id(word)] = vec\n",
    "    embedding[word2id[\"PAD\"]] = np.zeros((embed_size))\n",
    "    embedding[word2id[\"UNK\"]] = np.random.uniform(-1, 1, embed_size)\n",
    "    return embedding\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "def sentence_generator(X, embeddings, batch_size):\n",
    "    while True:\n",
    "        # loop once per epoch\n",
    "        num_recs = X.shape[0]\n",
    "        indices = np.random.permutation(np.arange(num_recs))\n",
    "        num_batches = num_recs // batch_size\n",
    "        for bid in range(num_batches):\n",
    "            sids = indices[bid * batch_size: (bid + 1) * batch_size]\n",
    "            Xbatch = embeddings[X[sids, :]]\n",
    "            yield Xbatch, Xbatch"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "def compute_cosine_similarity(x, y):\n",
    "    return np.dot(x, y) / (np.linalg.norm(x, 2) * np.linalg.norm(y, 2))\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "DATA_DIR = \"data\"\n",
    "\n",
    "def is_number(n):\n",
    "    temp = re.sub(\"[.,-/]\", \"\",n)\n",
    "    return temp.isdigit()\n",
    "\n",
    "# parsing sentences and building vocabulary\n",
    "word_freqs = collections.Counter()\n",
    "ftext = open(os.path.join(DATA_DIR, \"text.tsv\"), \"r\")\n",
    "sents = []\n",
    "sent_lens = []\n",
    "for line in ftext:\n",
    "    docid, text = line.strip().split(\"\\t\")\n",
    "    for sent in nltk.sent_tokenize(text):\n",
    "        for word in nltk.word_tokenize(sent):\n",
    "            if is_number(word):\n",
    "                word = \"9\"\n",
    "            word = word.lower()\n",
    "            word_freqs[word] += 1\n",
    "        sents.append(sent)\n",
    "        sent_lens.append(len(sent))\n",
    "ftext.close()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Total number of sentences are: 131545 \n",
      "Sentence distribution min 1, max 2434 , mean 120.525052, median 115.000000\n",
      "Vocab size (full) 50743\n"
     ]
    }
   ],
   "source": [
    "print(\"Total number of sentences are: {:d} \".format(len(sents)))\n",
    "print (\"Sentence distribution min {:d}, max {:d} , mean {:3f}, median {:3f}\".\n",
    "      format(np.min(sent_lens), np.max(sent_lens), np.mean(sent_lens), np.median(sent_lens)))\n",
    "print(\"Vocab size (full) {:d}\".format(len(word_freqs)))\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "VOCAB_SIZE = 5000\n",
    "EMBED_SIZE = 50\n",
    "LATENT_SIZE = 512\n",
    "SEQUENCE_LEN = 50\n",
    "\n",
    "BATCH_SIZE = 64\n",
    "NUM_EPOCHS = 20\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "vocabulary sizes: 5000 5000\n",
      "(5000, 50)\n",
      "number of sentences:  131545\n",
      "(92081, 50) (39464, 50)\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/home/am/anaconda3/envs/tf2p0alpha/lib/python3.6/site-packages/sklearn/model_selection/_split.py:2179: FutureWarning: From version 0.21, test_size will always complement train_size unless both are specified.\n",
      "  FutureWarning)\n"
     ]
    }
   ],
   "source": [
    "# word2id = collections.defaultdict(lambda: 1)\n",
    "word2id = {}\n",
    "word2id[\"PAD\"] = 0\n",
    "word2id[\"UNK\"] = 1\n",
    "for v, (k, _) in enumerate(word_freqs.most_common(VOCAB_SIZE - 2)):\n",
    "    word2id[k] = v + 2\n",
    "id2word = {v: k for k, v in word2id.items()}\n",
    "\n",
    "print(\"vocabulary sizes:\", len(word2id), len(id2word))\n",
    "\n",
    "sent_wids = [[lookup_word2id(w) for w in s.split()] for s in sents]\n",
    "sent_wids = sequence.pad_sequences(sent_wids, SEQUENCE_LEN)\n",
    "\n",
    "# load glove vectors into weight matrix\n",
    "embeddings = load_glove_vectors(os.path.join(\n",
    "    DATA_DIR, \"glove.6B.{:d}d.txt\".format(EMBED_SIZE)), word2id, EMBED_SIZE)\n",
    "print(embeddings.shape)\n",
    "\n",
    "# split sentences into training and test\n",
    "train_size = 0.7\n",
    "Xtrain, Xtest = train_test_split(sent_wids, train_size=train_size)\n",
    "print(\"number of sentences: \", len(sent_wids))\n",
    "print(Xtrain.shape, Xtest.shape)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "# define training and test generators\n",
    "train_gen = sentence_generator(Xtrain, embeddings, BATCH_SIZE)\n",
    "test_gen = sentence_generator(Xtest, embeddings, BATCH_SIZE)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "WARNING: Logging before flag parsing goes to stderr.\n",
      "W0827 08:36:53.456710 140327285733120 tf_logging.py:161] <tensorflow.python.keras.layers.recurrent.UnifiedLSTM object at 0x7f9fe809f588>: Note that this layer is not optimized for performance. Please use tf.keras.layers.CuDNNLSTM for better performance on GPU.\n",
      "W0827 08:36:53.468909 140327285733120 tf_logging.py:161] <tensorflow.python.keras.layers.recurrent.UnifiedLSTM object at 0x7f9fe655a390>: Note that this layer is not optimized for performance. Please use tf.keras.layers.CuDNNLSTM for better performance on GPU.\n",
      "W0827 08:36:54.271372 140327285733120 tf_logging.py:161] <tensorflow.python.keras.layers.recurrent.UnifiedLSTM object at 0x7f9fe5f31f28>: Note that this layer is not optimized for performance. Please use tf.keras.layers.CuDNNLSTM for better performance on GPU.\n",
      "W0827 08:36:54.273263 140327285733120 tf_logging.py:161] <tensorflow.python.keras.layers.recurrent.UnifiedLSTM object at 0x7f9fe64fd128>: Note that this layer is not optimized for performance. Please use tf.keras.layers.CuDNNLSTM for better performance on GPU.\n"
     ]
    }
   ],
   "source": [
    "# define autoencoder network\n",
    "inputs = Input(shape=(SEQUENCE_LEN, EMBED_SIZE), name=\"input\")\n",
    "encoded = Bidirectional(LSTM(LATENT_SIZE), merge_mode=\"sum\",\n",
    "                        name=\"encoder_lstm\")(inputs)\n",
    "decoded = RepeatVector(SEQUENCE_LEN, name=\"repeater\")(encoded)\n",
    "decoded = Bidirectional(LSTM(EMBED_SIZE, return_sequences=True),\n",
    "                        merge_mode=\"sum\",\n",
    "                        name=\"decoder_lstm\")(decoded)\n",
    "\n",
    "autoencoder = Model(inputs, decoded)\n",
    "\n",
    "#tensorboard = make_tensorboard(set_dir_name='rnn')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch 1/20\n",
      "1438/1438 [==============================] - 58s 40ms/step - loss: 0.1161 - val_loss: 0.1097\n",
      "Epoch 2/20\n",
      "1438/1438 [==============================] - 59s 41ms/step - loss: 0.1070 - val_loss: 0.1051\n",
      "Epoch 3/20\n",
      "1438/1438 [==============================] - 59s 41ms/step - loss: 0.1030 - val_loss: 0.1013\n",
      "Epoch 4/20\n",
      "1438/1438 [==============================] - 59s 41ms/step - loss: 0.0991 - val_loss: 0.0977\n",
      "Epoch 5/20\n",
      "1438/1438 [==============================] - 59s 41ms/step - loss: 0.0954 - val_loss: 0.0943\n",
      "Epoch 6/20\n",
      "1438/1438 [==============================] - 59s 41ms/step - loss: 0.0927 - val_loss: 0.0923\n",
      "Epoch 7/20\n",
      "1438/1438 [==============================] - 59s 41ms/step - loss: 0.0910 - val_loss: 0.0910\n",
      "Epoch 8/20\n",
      "1438/1438 [==============================] - 59s 41ms/step - loss: 0.0896 - val_loss: 0.0895\n",
      "Epoch 9/20\n",
      "1438/1438 [==============================] - 59s 41ms/step - loss: 0.0884 - val_loss: 0.0886\n",
      "Epoch 10/20\n",
      "1438/1438 [==============================] - 59s 41ms/step - loss: 0.0878 - val_loss: 0.0882\n",
      "Epoch 11/20\n",
      "1438/1438 [==============================] - 59s 41ms/step - loss: 0.0867 - val_loss: 0.0861\n",
      "Epoch 12/20\n",
      "1438/1438 [==============================] - 59s 41ms/step - loss: 0.0858 - val_loss: 0.0854\n",
      "Epoch 13/20\n",
      "1438/1438 [==============================] - 59s 41ms/step - loss: 0.0855 - val_loss: 0.0860\n",
      "Epoch 14/20\n",
      "1438/1438 [==============================] - 58s 41ms/step - loss: 0.0847 - val_loss: 0.0844\n",
      "Epoch 15/20\n",
      "1438/1438 [==============================] - 59s 41ms/step - loss: 0.0841 - val_loss: 0.0845\n",
      "Epoch 16/20\n",
      "1438/1438 [==============================] - 59s 41ms/step - loss: 0.0838 - val_loss: 0.0838\n",
      "Epoch 17/20\n",
      "1438/1438 [==============================] - 59s 41ms/step - loss: 0.0832 - val_loss: 0.0874\n",
      "Epoch 18/20\n",
      "1438/1438 [==============================] - 59s 41ms/step - loss: 0.0830 - val_loss: 0.0829\n",
      "Epoch 19/20\n",
      "1438/1438 [==============================] - 59s 41ms/step - loss: 0.0825 - val_loss: 0.0832\n",
      "Epoch 20/20\n",
      "1438/1438 [==============================] - 59s 41ms/step - loss: 0.0824 - val_loss: 0.0820\n"
     ]
    }
   ],
   "source": [
    "autoencoder.compile(optimizer=\"adam\", loss=\"mse\")\n",
    "\n",
    "# train\n",
    "num_train_steps = len(Xtrain) // BATCH_SIZE\n",
    "num_test_steps = len(Xtest) // BATCH_SIZE\n",
    "#checkpoint = ModelCheckpoint(\n",
    "#   filepath=os.path.join(DATA_DIR, \"sent-thoughts-autoencoder.h5\"),\n",
    "#    save_best_only=True)\n",
    "history = autoencoder.fit_generator(train_gen,\n",
    "                                    steps_per_epoch=num_train_steps,\n",
    "                                    epochs=NUM_EPOCHS,\n",
    "                                    validation_data=test_gen,\n",
    "                                    validation_steps=num_test_steps) \n",
    "                                    #callbacks=[checkpoint, tensorboard])\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [],
   "source": [
    "# collect autoencoder predictions for test set\n",
    "test_inputs, test_labels = next(test_gen)\n",
    "preds = autoencoder.predict(test_inputs)\n",
    "\n",
    "# extract encoder part from autoencoder\n",
    "encoder = Model(autoencoder.input,\n",
    "                autoencoder.get_layer(\"encoder_lstm\").output)\n",
    "# encoder.summary()\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<matplotlib.legend.Legend at 0x7f9fac10c898>"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.plot(history.history[\"loss\"], label = \"training loss\")\n",
    "plt.plot(history.history[\"val_loss\"], label = \"validation loss\")\n",
    "plt.xlabel(\"epochs\")\n",
    "plt.ylabel(\"Loss\")\n",
    "plt.legend()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0.984686553478241\n",
      "0.9815746545791626\n",
      "0.9793671369552612\n",
      "0.9805112481117249\n",
      "0.9630994200706482\n",
      "0.9790557622909546\n",
      "0.9893233180046082\n",
      "0.9869443774223328\n",
      "0.9665998220443726\n",
      "0.9893233180046082\n",
      "0.9829331040382385\n"
     ]
    }
   ],
   "source": [
    "# compute difference between vector produced by original and autoencoded\n",
    "k = 500\n",
    "cosims = np.zeros((k))\n",
    "i = 0\n",
    "for bid in range(num_test_steps):\n",
    "    xtest, ytest = next(test_gen)\n",
    "    ytest_ = autoencoder.predict(xtest)\n",
    "    Xvec = encoder.predict(xtest)\n",
    "    Yvec = encoder.predict(ytest_)\n",
    "    for rid in range(Xvec.shape[0]):\n",
    "        if i >= k:\n",
    "            break\n",
    "        cosims[i] = compute_cosine_similarity(Xvec[rid], Yvec[rid])\n",
    "        if i <= 10:\n",
    "            print(cosims[i])\n",
    "        i += 1\n",
    "    if i >= k:\n",
    "        break"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.hist(cosims, bins=10, density=True)\n",
    "plt.xlabel(\"cosine similarity\")\n",
    "plt.ylabel(\"frequency\")\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python [conda env:anaconda3]",
   "language": "python",
   "name": "conda-env-anaconda3-py"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.8"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
